package com.starlinkdt.service.task;

import com.starlinkdt.common.core.redis.RedisCache;
import com.starlinkdt.common.domain.system.entity.TaskDispatch;
import com.starlinkdt.common.enums.BusinessTypeEnum;
import com.starlinkdt.common.enums.TaskStatusEnum;
import com.starlinkdt.common.enums.TaskTypeEnum;
import com.starlinkdt.service.system.ITaskDispatchService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import static com.starlinkdt.common.constant.CacheConstants.TASK_RETRY_LOCK_KEY;


/**
 * 任务调度处理器
 */
@Slf4j
@Component("TaskDispatchHandle")
public abstract class TaskDispatchHandle {

    @Autowired
    private ITaskDispatchService taskService;

    @Autowired
    private RedisCache redisCache;
    /**
     * 批处理大小
     */
    public static int BATCH_SIZE = 1000;
    /**
     * 最大尝试次数
     */
    public static int TRY_TIMES = 3;
    /**
     * 任务类型 默认内部
     */
    public static int TASK_TYPE = TaskTypeEnum.INTERNAL.getCode();
    /**
     * 业务类型 默认默认业务
     */
    public static String BUSINESS_TYPE = BusinessTypeEnum.DEFAULT.getCode();


    /**
     * 扫描未执行的请求，进行批处理
     */
    public final void handleRequest() {
        List<TaskDispatch> tasks;
        do {
            // 分页获取待处理任务
            tasks = taskService.getPendingTasks(BATCH_SIZE, TASK_TYPE, BUSINESS_TYPE);
            if (tasks.isEmpty()) {
                break;
            }
            // 执行批量处理
            batchProcess(tasks);
        } while (tasks.size() == BATCH_SIZE); // 如果不满一页说明已全部处理完
    }


    /**
     * 扫描失败的请求，进行重试
     */
    public final void compensateRequest() {
        List<TaskDispatch> failTasks;
        int pageNum = 1;
        // 获取失败重试的任务
        do {
            // 分页获取可重试的任务
            failTasks = taskService.getFailedTasksForRetry(pageNum, BATCH_SIZE, TASK_TYPE, BUSINESS_TYPE, TRY_TIMES);
            if (failTasks.isEmpty()) {
                break;
            }
            // 批量处理失败任务
            compensateProcess(failTasks);
            pageNum++;
        } while (failTasks.size() == BATCH_SIZE);
        List<TaskDispatch> processTasks;
        // 获取长时间执行中的任务
        do {
            // 分页查询长期处于进行中的任务
            processTasks = taskService.getProcessingTasksForRetry(BATCH_SIZE, TASK_TYPE, BUSINESS_TYPE);
            if (processTasks.isEmpty()) {
                break;
            }
            // 批量恢复为待处理
            taskService.batchMarkAsPending(processTasks);
        } while (processTasks.size() == BATCH_SIZE);
    }

    /**
     * 批量处理失败任务
     */
    private void compensateProcess(List<TaskDispatch> failTasks) {
        List<TaskDispatch> reTryTasks = new ArrayList<>();
        // 判断是否需要重试
        for (TaskDispatch task : failTasks) {
            // 先确认当前业务ID的最新任务是失败的（可重试）
            Optional<TaskDispatch> latestTaskOpt = taskService.getLatestFailedTask(task.getBusinessId());
            if (latestTaskOpt.isEmpty()) {
                continue; // 非失败任务不重试
            }
            // 分布式锁
            String businessId = task.getBusinessId();
            String lockKey = TASK_RETRY_LOCK_KEY + businessId;
            // 获取锁，设置过期时间为10秒
            if (redisCache.tryLock(lockKey, 10)) {
                try {
                    // 再次检查是否已经存在待处理任务
                    boolean exists = taskService.existsPendingTask(businessId);
                    if (exists) {
                        continue;
                    }
                    // 复制一个task 将状态改为pending 重试次数+1 新增
                    TaskDispatch reTryTask = new TaskDispatch();
                    reTryTask.setTaskType(task.getTaskType());
                    reTryTask.setBusinessId(task.getBusinessId());
                    reTryTask.setBusinessType(task.getBusinessType());
                    reTryTask.setRequestJson(task.getRequestJson());
                    reTryTask.setStatus(TaskStatusEnum.PENDING.getCode());
                    reTryTask.setRetryCount(task.getRetryCount() + 1);
                    reTryTask.setVersion(0);
                    reTryTask.setMaxRetry(task.getMaxRetry());
                    reTryTasks.add(reTryTask);
                } finally {
                    // 释放锁
                    redisCache.releaseLock(lockKey);
                }
            } else {
                // 获取锁失败，跳过这个 businessId
                log.warn("Failed to acquire lock for businessId: {}", businessId);
            }
        }
        if (!CollectionUtils.isEmpty(reTryTasks)) {
            taskService.saveBatch(reTryTasks);
        }
    }


    /**
     * 批量执行任务并分类成功/失败
     */
    private void batchProcess(List<TaskDispatch> tasks) {
        // 先批量标记为 processing
        if (!taskService.batchMarkAsProcessing(tasks)) {
            // 如果标记失败（可能是版本号冲突），跳过这批任务
            return;
        }
        // 手动刷新version
        for (TaskDispatch task : tasks) {
            task.setVersion(task.getVersion() + 1);
        }
        // 剩余无法再重试的任务
        List<TaskDispatch> noHandledList = tasks.stream()
                .filter(task -> task.getRetryCount() >= TRY_TIMES)
                .toList();
        // 过滤出可以继续重试的任务
        List<TaskDispatch> handledList = tasks.stream()
                .filter(task -> task.getRetryCount() < TRY_TIMES)
                .toList();
        // 不可重试的任务更改状态为 不可重试
        if (!CollectionUtils.isEmpty(noHandledList)) {
            taskService.batchMarkAsNoHandled(noHandledList);
        }
        List<TaskDispatch> successList = new ArrayList<>();
        List<TaskDispatch> failList = new ArrayList<>();
        // 执行业务处理
        business(handledList, successList, failList);
        // 批量更新成功任务
        if (!successList.isEmpty()) {
            taskService.batchMarkAsSuccess(successList);
        }
        // 批量更新失败任务
        if (!failList.isEmpty()) {
            taskService.batchMarkAsFailed(failList);
        }
    }

    /**
     * 业务处理
     */
    public abstract void business(List<TaskDispatch> tasks, List<TaskDispatch> successList, List<TaskDispatch> failList);
}


